package general.CPA;

import general.ChannelAgent;
import general.CPA.ArrayCPA;
import general.CPA.CPA;
import general.conflictset.ConflictSet;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Iterator;

public class DynamicCPA extends ArrayCPA {

	byte[] order;
	byte[] reverseOrder;
	byte numAssigned = 0;

	public DynamicCPA() {
	}

	public DynamicCPA(int n, int cpaId, int creator) {
		super(n, cpaId, creator);
		this.order = new byte[n];
		this.reverseOrder = new byte[n];
		this.numAssigned = 0;
		for (int i=0; i<n; i++){
			order[i] = -1;
			reverseOrder[i] = -1;
		}
		resetSteps();
	}

	protected DynamicCPA(int id, int steps, int creator, byte[] cpa, byte[] order){
		super(id, steps, creator, cpa);
		this.order = order;
		this.reverseOrder = new byte[order.length];
		Arrays.fill(reverseOrder, (byte)-1);
		for (byte i=0; i<assignment.length; i++){	
			if (assignment[i]!=-1){
				numAssigned++;
			}
			if (order[i]!=-1){
				reverseOrder[order[i]] = i;
			}
		}
	}


	@Override
	public CPA createNew(int n, int cpaId, int creator) {
		return new DynamicCPA(n, cpaId, creator); 
	}

	@Override
	public CPA createNew(int id, int steps, int creator, byte[] cpa, byte[] order) {
		return new DynamicCPA(id , steps, creator, cpa, order);
	}

	@Override
	public CPA duplicate(int id, byte creator) {
		byte[] assignment2 = Arrays.copyOf(assignment, n);
		byte[] order2 = Arrays.copyOf(order, n);
		return new DynamicCPA(id, 0, creator, assignment2, order2);
	}

	@Override
	public byte[] getBytes() {
		ByteBuffer buf = ByteBuffer.allocate(4+4+4+n+n);
		buf.putInt(getCpaId());
		buf.putInt(getCreator());
		buf.putInt(getSteps());
		buf.put(assignment);
		buf.put(order);
		return buf.array();
	}

	@Override
	public void insert(byte var, byte value) {
		if (isAssigned(var)) {
			new Exception().printStackTrace(); 
			return;
		}

		super.insert(var, value);
		order[var] = numAssigned;
		reverseOrder[numAssigned] = var;
		numAssigned++;
	}

	@Override
	public void remove(byte i) {
		if (!isAssigned(i)) return;
		super.remove(i);
		for (int k=order[i]; k<numAssigned; k++){	//shifting
			reverseOrder[k] = reverseOrder[k+1];
			if (reverseOrder[k]!=-1) order[reverseOrder[k]]--;
		}
		reverseOrder[numAssigned]=-1;
		order[i] = -1;
		numAssigned--;
	}

	@Override
	public Iterator<Integer> iterator() {
		return new MyIterator();
	}

	private class MyIterator implements Iterator<Integer>{
		byte i = 0;
		public MyIterator() {
			//			while (i<numAssigned){	i++; }
		}

		@Override
		public boolean hasNext() {
			return i<numAssigned;
		}

		@Override
		public Integer next() {
			int tmp = reverseOrder[i];
			i++;
			return tmp;
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException("remove() unsupported for ArrayCurrentDomain");
		}
	}

	@Override
	public String toString() {
		if (reverseOrder==null) return super.toString();
		String str = "";
		str+=("[");
		for (int i=0; i<n-1; i++){
			str+=(assignment[i] +", ");
		}
		str+=(assignment[n-1]+"] ");
		str+=("[");
		for (int i=0; i<n-1; i++){
			str+=(reverseOrder[i] +", ");
		}
		str+=(reverseOrder[n-1]+"] "+ getId().toString());
		str+=("[");
		for (int i=0; i<n-1; i++){
			str+=(order[i] +", ");
		}
		str+=(order[n-1]+"] "+ getId().toString());
		return str;
	}

	@Override
	public byte getType() {
		return ChannelAgent.SEND_DYNAMICCPA;
	}

	public byte[] between(byte low, byte high) {
		byte orderLow = (byte)(order[low]+1);
		byte orderHigh = numAssigned;
		byte[] ans = new byte[orderHigh-orderLow];
		for (int i=0; i< orderHigh-orderLow; i++){
			ans[i] = reverseOrder[orderLow+i];	//the agent ordered i
		}
		return ans;
	}

	public void removeBigger(ConflictSet conflicts, byte id) {
		byte agentOrder = order[id];
		if (agentOrder!=-1){
			agentOrder++;
			for (byte i = agentOrder; i<numAssigned; i++){
				conflicts.remove(reverseOrder[i]);
			}
		}
	}

	public byte previous(byte id) {
		if (order[id]!=-1){
			return reverseOrder[order[id]-1];
		}else{
			return reverseOrder[numAssigned-1];
		}
	}

	public void removeBigger(byte h) {
		byte agentOrder = order[h];
		for (byte i = agentOrder; i<numAssigned; i++){
			remove(reverseOrder[i]);
		}
	}

	public byte max(byte id, ConflictSet conflicts){
		byte maxOrder = -1;
		byte num = order[id];
		if (order[id]==-1){
			num = numAssigned;
		}
		for (byte i=0; i<num; i++){
			if (conflicts.isConflicting(reverseOrder[i])){
				maxOrder = reverseOrder[i];
			}
		}
		return maxOrder;
	}

	public boolean hasNext(byte agent) {
		return reverseOrder[order[agent]+1]!=-1;
	}

	public byte next(byte agent) {
		return reverseOrder[order[agent]+1];
	}

	public byte[] getUnassigned() {
		byte[] ans = new byte[n-numAssigned];
		int count = 0;
		for (byte i=0; i<n; i++) if (!isAssigned(i)){
			ans[count] = i;
			count++;
		}
		return ans;
	}

	public boolean isBefore(byte cpaCreator, byte maxConflicted) {
		if (order[cpaCreator]==-1) return false;
		if (order[maxConflicted]==-1) return true;
		if (order[cpaCreator] < order[maxConflicted]) return true;
		else return false;
	}
}